﻿using Furion.CMS.Application.Dto;
using Furion.CMS.Core;
using Furion.CMS.Core.Base;
using Furion.CMS.Core.EventSource;
using Furion.CMS.Core.Permission;
using Furion.CMS.Domain;
using Furion.CMS.EntityFramework.Core;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using Furion.EventBus;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.Application
{
    /// <summary>
    /// 角色应用服务
    /// </summary>
    [DisplayName("RoleManager")]
    public class RoleAppService : CurdAuditAppService<RoleInfo, int, RoleCreateAndUpdateDto, RoleCreateAndUpdateDto, RoleDto, RoleDetailDto, RoleFilter, RoleSort>, IScoped
    {
        private readonly IRepository<PermissionInfo> _permissionRepository;
        private readonly IRepository<UserInfo> _userRepository;
        private readonly IEventPublisher _eventCenter;

        public RoleAppService(IRepository<RoleInfo> repository,
            IRepository<PermissionInfo> permissionRepository,
            IRepository<UserInfo> userRepository,
            IEventPublisher eventCenter)
            : base(repository)
        {
            this._permissionRepository = permissionRepository;
            this._userRepository = userRepository;
            this._eventCenter = eventCenter;
        }

        /// <summary>
        /// 获取全部角色
        /// </summary>
        /// <returns></returns>
        public List<RoleDto> GetAll()
        {
            return _appRepository.AsQueryable(false).Adapt<List<RoleDto>>();
        }

        protected override RoleInfo BeforeUpdate(RoleCreateAndUpdateDto input, RoleInfo oldEntity, RoleInfo newEntity, params string[] ignoreProperties)
        {
            return base.BeforeUpdate(input, oldEntity, newEntity, "HasSeed", "Code");
        }

        protected override void BeforeDelete(IList<int> ids, IQueryable<RoleInfo> entities)
        {
            var canNotDeleteList = entities.Where(t => t.HasSeed).Select(s => s.Name).ToList();

            if (canNotDeleteList.Any())
            {
                throw Oops.Oh($"{string.Join(",", canNotDeleteList)} 为内置角色,无法删除");
            }

            base.BeforeDelete(ids, entities);
        }

        /// <summary>
        /// 获取权限配置详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [SecurityDefine]
        public async Task<RoleConfigureDetailDto> GetConfigre(int id)
        {
            var configureDetails = await _appRepository.Where(t => t.Id == id, false)
                 .Include(i => i.Permissions)
                 .FirstOrDefaultAsync();

            return configureDetails.Adapt<RoleConfigureDetailDto>();
        }

        /// <summary>
        /// 更新权限配置
        /// </summary>
        /// <param name="id"></param>
        /// <param name="permissionCodes"></param>
        [UnitOfWork]
        [SecurityDefine]
        public void UpdateConfigre([ApiSeat(ApiSeats.ActionStart)] int id, [FromBody] List<string> permissionCodes)
        {
            var update = _appRepository.Where(t => t.Id == id)
                .Include(i => i.Permissions)
                .Include(i => i.Users)
                .AsSplitQuery()
                .FirstOrDefault();

            if (update == null) throw Oops.Bah($"未能找到ID为 {id} 的数据");
            // 更新权限表
            var updateConfigreList = _permissionRepository.Where(t => permissionCodes.Contains(t.Code)).ToList();

            // 更新数据库
            update.Permissions.ToCollectionChange(updateConfigreList, f => f.Code, out List<PermissionInfo> insertItems, out List<PermissionInfo> removeItems);

            // 通知前端更换token
            var triggerEvetns = update.Users.Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.Id.ToString()));
            _eventCenter?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);
        }

        /// <summary>
        /// 获取角色授权用户
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [SecurityDefine]
        public List<UserDto> GetUsers([ApiSeat(ApiSeats.ActionStart)] int id)
        {
            return _appRepository.AsQueryable(false)
                  .Where(t => t.Id == id)
                  .Include(i => i.Users)
                  .SelectMany(s => s.Users)
                  .Adapt<List<UserDto>>();
        }

        /// <summary>
        /// 更新角色的授权人员
        /// </summary>
        /// <param name="id"></param>
        /// <param name="relatedList"></param>
        /// <returns></returns>
        [UnitOfWork]
        [SecurityDefine]
        public void UpdateUsers([ApiSeat(ApiSeats.ActionStart)] int id, [FromBody] List<int> relatedList)
        {
            var entity = _appRepository
                .Where(x => x.Id == id)
                //.Include(i => i.Permissions)
                .Include(i => i.Users)
                .FirstOrDefault();

            if (entity == null) throw Oops.Oh($"未能找到ID为 {id} 的数据");

            var relatedEntitys = _userRepository.Where(t => relatedList.Contains(t.Id)).ToList();
            entity.Users.ToCollectionChange(relatedEntitys, f => f.Id, out List<UserInfo> insertItems, out List<UserInfo> removeUsers);

            // 通知前端更换token
            var triggerEvetns = insertItems.Union(removeUsers).Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.Id.ToString()));
            _eventCenter?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);
        }

        /// <summary>
        /// 获取角色权限
        /// </summary>
        /// <returns></returns>
        [ApiDescriptionSettings(false)]
        public async Task<List<KeyValuePair<string, IEnumerable<string>>>> GetRolePermission()
        {
            var result = new Dictionary<string, IEnumerable<string>>();
            var fullRoleList = await _appRepository.AsQueryable(false)
                .Include(a => a.Users)
                .Include(a => a.Permissions)
                .ThenInclude(a => a.Users)
                .AsSplitQuery()
                .ToListAsync();

            foreach (var role in fullRoleList)
            {
                var roleAuthorizedUserIdList = role.Users.Select(s => string.Format(CacheKeyConsts.PermissionResourceValue, role.Code, s.Id));
                var dependentResources = role.Permissions.Where(t => !string.IsNullOrWhiteSpace(t.DependentResources))
                     .SelectMany(s => s.DependentResources.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)).Distinct();

                foreach (var resourceId in dependentResources)
                {
                    var resourceKey = string.Format(CacheKeyConsts.PermissionResource, resourceId, "");

                    if (result.TryGetValue(resourceKey, out IEnumerable<string> userIds))
                    {
                        userIds = userIds.Union(roleAuthorizedUserIdList).Distinct();
                    }
                    else
                    {
                        result.TryAdd(resourceKey, roleAuthorizedUserIdList);
                    }
                }
            }

            return result.ToList();
        }
    }
}
